1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.codehaus.groovy.vmplugin.v5;
20
21 import org.codehaus.groovy.GroovyBugError;
22 import org.codehaus.groovy.ast.AnnotatedNode;
23 import org.codehaus.groovy.ast.AnnotationNode;
24 import org.codehaus.groovy.ast.ClassHelper;
25 import org.codehaus.groovy.ast.ClassNode;
26 import org.codehaus.groovy.ast.CompileUnit;
27 import org.codehaus.groovy.ast.FieldNode;
28 import org.codehaus.groovy.ast.GenericsType;
29 import org.codehaus.groovy.ast.MethodNode;
30 import org.codehaus.groovy.ast.PackageNode;
31 import org.codehaus.groovy.ast.Parameter;
32 import org.codehaus.groovy.ast.expr.ClassExpression;
33 import org.codehaus.groovy.ast.expr.ConstantExpression;
34 import org.codehaus.groovy.ast.expr.Expression;
35 import org.codehaus.groovy.ast.expr.ListExpression;
36 import org.codehaus.groovy.ast.expr.PropertyExpression;
37 import org.codehaus.groovy.ast.stmt.ReturnStatement;
38 import org.codehaus.groovy.vmplugin.VMPlugin;
39
40 import java.lang.annotation.Annotation;
41 import java.lang.annotation.ElementType;
42 import java.lang.annotation.Retention;
43 import java.lang.annotation.RetentionPolicy;
44 import java.lang.annotation.Target;
45 import java.lang.reflect.Array;
46 import java.lang.reflect.Constructor;
47 import java.lang.reflect.Field;
48 import java.lang.reflect.GenericArrayType;
49 import java.lang.reflect.InvocationTargetException;
50 import java.lang.reflect.Method;
51 import java.lang.reflect.ParameterizedType;
52 import java.lang.reflect.Type;
53 import java.lang.reflect.TypeVariable;
54 import java.lang.reflect.WildcardType;
55 import java.util.List;
56
57
58
59
60
61
62 public class Java5 implements VMPlugin {
63 private static Class[] EMPTY_CLASS_ARRAY = new Class[0];
64 private static final Class[] PLUGIN_DGM = {PluginDefaultGroovyMethods.class};
65
66 public void setAdditionalClassInformation(ClassNode cn) {
67 setGenericsTypes(cn);
68 }
69
70 private void setGenericsTypes(ClassNode cn) {
71 TypeVariable[] tvs = cn.getTypeClass().getTypeParameters();
72 GenericsType[] gts = configureTypeVariable(tvs);
73 cn.setGenericsTypes(gts);
74 }
75
76 private GenericsType[] configureTypeVariable(TypeVariable[] tvs) {
77 if (tvs.length == 0) return null;
78 GenericsType[] gts = new GenericsType[tvs.length];
79 for (int i = 0; i < tvs.length; i++) {
80 gts[i] = configureTypeVariableDefinition(tvs[i]);
81 }
82 return gts;
83 }
84
85 private GenericsType configureTypeVariableDefinition(TypeVariable tv) {
86 ClassNode base = configureTypeVariableReference(tv);
87 ClassNode redirect = base.redirect();
88 base.setRedirect(null);
89 Type[] tBounds = tv.getBounds();
90 GenericsType gt;
91 if (tBounds.length == 0) {
92 gt = new GenericsType(base);
93 } else {
94 ClassNode[] cBounds = configureTypes(tBounds);
95 gt = new GenericsType(base, cBounds, null);
96 gt.setName(base.getName());
97 gt.setPlaceholder(true);
98 }
99 base.setRedirect(redirect);
100 return gt;
101 }
102
103 private ClassNode[] configureTypes(Type[] types) {
104 if (types.length == 0) return null;
105 ClassNode[] nodes = new ClassNode[types.length];
106 for (int i = 0; i < types.length; i++) {
107 nodes[i] = configureType(types[i]);
108 }
109 return nodes;
110 }
111
112 private ClassNode configureType(Type type) {
113 if (type instanceof WildcardType) {
114 return configureWildcardType((WildcardType) type);
115 } else if (type instanceof ParameterizedType) {
116 return configureParameterizedType((ParameterizedType) type);
117 } else if (type instanceof GenericArrayType) {
118 return configureGenericArray((GenericArrayType) type);
119 } else if (type instanceof TypeVariable) {
120 return configureTypeVariableReference((TypeVariable) type);
121 } else if (type instanceof Class) {
122 return configureClass((Class) type);
123 } else if (type==null) {
124 throw new GroovyBugError("Type is null. Most probably you let a transform reuse existing ClassNodes with generics information, that is now used in a wrong context.");
125 } else {
126 throw new GroovyBugError("unknown type: " + type + " := " + type.getClass());
127 }
128 }
129
130 private ClassNode configureClass(Class c) {
131 if (c.isPrimitive()) {
132 return ClassHelper.make(c);
133 } else {
134 return ClassHelper.makeWithoutCaching(c, false);
135 }
136 }
137
138 private ClassNode configureGenericArray(GenericArrayType genericArrayType) {
139 Type component = genericArrayType.getGenericComponentType();
140 ClassNode node = configureType(component);
141 return node.makeArray();
142 }
143
144 private ClassNode configureWildcardType(WildcardType wildcardType) {
145 ClassNode base = ClassHelper.makeWithoutCaching("?");
146 base.setRedirect(ClassHelper.OBJECT_TYPE);
147
148 ClassNode[] lowers = configureTypes(wildcardType.getLowerBounds());
149 ClassNode lower = null;
150
151 if (lowers != null) lower = lowers[0];
152
153 ClassNode[] upper = configureTypes(wildcardType.getUpperBounds());
154 GenericsType t = new GenericsType(base, upper, lower);
155 t.setWildcard(true);
156
157 ClassNode ref = ClassHelper.makeWithoutCaching(Object.class, false);
158 ref.setGenericsTypes(new GenericsType[]{t});
159
160 return ref;
161 }
162
163 private ClassNode configureParameterizedType(ParameterizedType parameterizedType) {
164 ClassNode base = configureType(parameterizedType.getRawType());
165 GenericsType[] gts = configureTypeArguments(parameterizedType.getActualTypeArguments());
166 base.setGenericsTypes(gts);
167 return base;
168 }
169
170 private ClassNode configureTypeVariableReference(TypeVariable tv) {
171 ClassNode cn = ClassHelper.makeWithoutCaching(tv.getName());
172 cn.setGenericsPlaceHolder(true);
173 ClassNode cn2 = ClassHelper.makeWithoutCaching(tv.getName());
174 cn2.setGenericsPlaceHolder(true);
175 GenericsType[] gts = new GenericsType[]{new GenericsType(cn2)};
176 cn.setGenericsTypes(gts);
177 cn.setRedirect(ClassHelper.OBJECT_TYPE);
178 return cn;
179 }
180
181 private GenericsType[] configureTypeArguments(Type[] ta) {
182 if (ta.length == 0) return null;
183 GenericsType[] gts = new GenericsType[ta.length];
184 for (int i = 0; i < ta.length; i++) {
185 ClassNode t = configureType(ta[i]);
186 if (ta[i] instanceof WildcardType) {
187 GenericsType[] gen = t.getGenericsTypes();
188 gts[i] = gen[0];
189 } else {
190 gts[i] = new GenericsType(t);
191 }
192 }
193 return gts;
194 }
195
196 public Class[] getPluginDefaultGroovyMethods() {
197 return PLUGIN_DGM;
198 }
199
200 public Class[] getPluginStaticGroovyMethods() {
201 return EMPTY_CLASS_ARRAY;
202 }
203
204 private void setAnnotationMetaData(Annotation[] annotations, AnnotatedNode an) {
205 for (Annotation annotation : annotations) {
206 AnnotationNode node = new AnnotationNode(ClassHelper.make(annotation.annotationType()));
207 configureAnnotation(node, annotation);
208 an.addAnnotation(node);
209 }
210 }
211
212 private void configureAnnotationFromDefinition(AnnotationNode definition, AnnotationNode root) {
213 ClassNode type = definition.getClassNode();
214 if (!type.isResolved()) return;
215 Class clazz = type.getTypeClass();
216 if (clazz == Retention.class) {
217 Expression exp = definition.getMember("value");
218 if (!(exp instanceof PropertyExpression)) return;
219 PropertyExpression pe = (PropertyExpression) exp;
220 String name = pe.getPropertyAsString();
221 RetentionPolicy policy = RetentionPolicy.valueOf(name);
222 setRetentionPolicy(policy, root);
223 } else if (clazz == Target.class) {
224 Expression exp = definition.getMember("value");
225 if (!(exp instanceof ListExpression)) return;
226 ListExpression le = (ListExpression) exp;
227 int bitmap = 0;
228 for (Expression e : le.getExpressions()) {
229 if (!(e instanceof PropertyExpression)) return;
230 PropertyExpression element = (PropertyExpression) e;
231 String name = element.getPropertyAsString();
232 ElementType value = ElementType.valueOf(name);
233 bitmap |= getElementCode(value);
234 }
235 root.setAllowedTargets(bitmap);
236 }
237 }
238
239 public void configureAnnotation(AnnotationNode node) {
240 ClassNode type = node.getClassNode();
241 List<AnnotationNode> annotations = type.getAnnotations();
242 for (AnnotationNode an : annotations) {
243 configureAnnotationFromDefinition(an, node);
244 }
245 configureAnnotationFromDefinition(node, node);
246 }
247
248 private void configureAnnotation(AnnotationNode node, Annotation annotation) {
249 Class type = annotation.annotationType();
250 if (type == Retention.class) {
251 Retention r = (Retention) annotation;
252 RetentionPolicy value = r.value();
253 setRetentionPolicy(value, node);
254 node.setMember("value", new PropertyExpression(
255 new ClassExpression(ClassHelper.makeWithoutCaching(RetentionPolicy.class, false)),
256 value.toString()));
257 } else if (type == Target.class) {
258 Target t = (Target) annotation;
259 ElementType[] elements = t.value();
260 ListExpression elementExprs = new ListExpression();
261 for (ElementType element : elements) {
262 elementExprs.addExpression(new PropertyExpression(
263 new ClassExpression(ClassHelper.ELEMENT_TYPE_TYPE), element.name()));
264 }
265 node.setMember("value", elementExprs);
266 } else {
267 Method[] declaredMethods;
268 try {
269 declaredMethods = type.getDeclaredMethods();
270 } catch (SecurityException se) {
271 declaredMethods = new Method[0];
272 }
273 for (Method declaredMethod : declaredMethods) {
274 try {
275 Object value = declaredMethod.invoke(annotation);
276 Expression valueExpression = annotationValueToExpression(value);
277 if (valueExpression == null)
278 continue;
279 node.setMember(declaredMethod.getName(), valueExpression);
280 } catch (IllegalAccessException e) {
281 } catch (InvocationTargetException e) {
282 }
283 }
284 }
285 }
286
287 private Expression annotationValueToExpression (Object value) {
288 if (value == null || value instanceof String || value instanceof Number || value instanceof Character || value instanceof Boolean)
289 return new ConstantExpression(value);
290
291 if (value instanceof Class)
292 return new ClassExpression(ClassHelper.makeWithoutCaching((Class)value));
293
294 if (value.getClass().isArray()) {
295 ListExpression elementExprs = new ListExpression();
296 int len = Array.getLength(value);
297 for (int i = 0; i != len; ++i)
298 elementExprs.addExpression(annotationValueToExpression(Array.get(value, i)));
299 return elementExprs;
300 }
301
302 return null;
303 }
304
305 private void setRetentionPolicy(RetentionPolicy value, AnnotationNode node) {
306 switch (value) {
307 case RUNTIME:
308 node.setRuntimeRetention(true);
309 break;
310 case SOURCE:
311 node.setSourceRetention(true);
312 break;
313 case CLASS:
314 node.setClassRetention(true);
315 break;
316 default:
317 throw new GroovyBugError("unsupported Retention " + value);
318 }
319 }
320
321 private int getElementCode(ElementType value) {
322 switch (value) {
323 case TYPE:
324 return AnnotationNode.TYPE_TARGET;
325 case CONSTRUCTOR:
326 return AnnotationNode.CONSTRUCTOR_TARGET;
327 case METHOD:
328 return AnnotationNode.METHOD_TARGET;
329 case FIELD:
330 return AnnotationNode.FIELD_TARGET;
331 case PARAMETER:
332 return AnnotationNode.PARAMETER_TARGET;
333 case LOCAL_VARIABLE:
334 return AnnotationNode.LOCAL_VARIABLE_TARGET;
335 case ANNOTATION_TYPE:
336 return AnnotationNode.ANNOTATION_TARGET;
337 case PACKAGE:
338 return AnnotationNode.PACKAGE_TARGET;
339 default:
340 throw new GroovyBugError("unsupported Target " + value);
341 }
342 }
343
344 private void setMethodDefaultValue(MethodNode mn, Method m) {
345 Object defaultValue = m.getDefaultValue();
346 ConstantExpression cExp = ConstantExpression.NULL;
347 if (defaultValue!=null) cExp = new ConstantExpression(defaultValue);
348 mn.setCode(new ReturnStatement(cExp));
349 mn.setAnnotationDefault(true);
350 }
351
352 public void configureClassNode(CompileUnit compileUnit, ClassNode classNode) {
353 try {
354 Class clazz = classNode.getTypeClass();
355 Field[] fields = clazz.getDeclaredFields();
356 for (Field f : fields) {
357 ClassNode ret = makeClassNode(compileUnit, f.getGenericType(), f.getType());
358 FieldNode fn = new FieldNode(f.getName(), f.getModifiers(), ret, classNode, null);
359 setAnnotationMetaData(f.getAnnotations(), fn);
360 classNode.addField(fn);
361 }
362 Method[] methods = clazz.getDeclaredMethods();
363 for (Method m : methods) {
364 ClassNode ret = makeClassNode(compileUnit, m.getGenericReturnType(), m.getReturnType());
365 Parameter[] params = makeParameters(compileUnit, m.getGenericParameterTypes(), m.getParameterTypes(), m.getParameterAnnotations());
366 ClassNode[] exceptions = makeClassNodes(compileUnit, m.getGenericExceptionTypes(), m.getExceptionTypes());
367 MethodNode mn = new MethodNode(m.getName(), m.getModifiers(), ret, params, exceptions, null);
368 mn.setSynthetic(m.isSynthetic());
369 setMethodDefaultValue(mn, m);
370 setAnnotationMetaData(m.getAnnotations(), mn);
371 mn.setGenericsTypes(configureTypeVariable(m.getTypeParameters()));
372 classNode.addMethod(mn);
373 }
374 Constructor[] constructors = clazz.getDeclaredConstructors();
375 for (Constructor ctor : constructors) {
376 Parameter[] params = makeParameters(compileUnit, ctor.getGenericParameterTypes(), ctor.getParameterTypes(), ctor.getParameterAnnotations());
377 ClassNode[] exceptions = makeClassNodes(compileUnit, ctor.getGenericExceptionTypes(), ctor.getExceptionTypes());
378 classNode.addConstructor(ctor.getModifiers(), params, exceptions, null);
379 }
380
381 Class sc = clazz.getSuperclass();
382 if (sc != null) classNode.setUnresolvedSuperClass(makeClassNode(compileUnit, clazz.getGenericSuperclass(), sc));
383 makeInterfaceTypes(compileUnit, classNode, clazz);
384 setAnnotationMetaData(classNode.getTypeClass().getAnnotations(), classNode);
385
386 PackageNode packageNode = classNode.getPackage();
387 if (packageNode != null) {
388 setAnnotationMetaData(classNode.getTypeClass().getPackage().getAnnotations(), packageNode);
389 }
390 } catch (NoClassDefFoundError e) {
391 throw new NoClassDefFoundError("Unable to load class "+classNode.toString(false)+" due to missing dependency "+e.getMessage());
392 }
393 }
394
395 private void makeInterfaceTypes(CompileUnit cu, ClassNode classNode, Class clazz) {
396 Type[] interfaceTypes = clazz.getGenericInterfaces();
397 if (interfaceTypes.length == 0) {
398 classNode.setInterfaces(ClassNode.EMPTY_ARRAY);
399 } else {
400 ClassNode[] ret = new ClassNode[interfaceTypes.length];
401 for (int i = 0; i < interfaceTypes.length; i++) {
402 Type type = interfaceTypes[i];
403 while (!(type instanceof Class)) {
404 ParameterizedType pt = (ParameterizedType) type;
405 Type t2 = pt.getRawType();
406 if (t2==type) {
407 throw new GroovyBugError("Cannot transform generic signature of "+clazz+
408 " with generic interface "+interfaceTypes[i]+" to a class.");
409 }
410 type = t2;
411 }
412 ret[i] = makeClassNode(cu, interfaceTypes[i], (Class) type);
413 }
414 classNode.setInterfaces(ret);
415 }
416 }
417
418 private ClassNode[] makeClassNodes(CompileUnit cu, Type[] types, Class[] cls) {
419 ClassNode[] nodes = new ClassNode[types.length];
420 for (int i = 0; i < nodes.length; i++) {
421 nodes[i] = makeClassNode(cu, types[i], cls[i]);
422 }
423 return nodes;
424 }
425
426 private ClassNode makeClassNode(CompileUnit cu, Type t, Class c) {
427 ClassNode back = null;
428 if (cu != null) back = cu.getClass(c.getName());
429 if (back == null) back = ClassHelper.make(c);
430 if (!(t instanceof Class)) {
431 ClassNode front = configureType(t);
432 front.setRedirect(back);
433 return front;
434 }
435 return back.getPlainNodeReference();
436 }
437
438 private Parameter[] makeParameters(CompileUnit cu, Type[] types, Class[] cls, Annotation[][] parameterAnnotations) {
439 Parameter[] params = Parameter.EMPTY_ARRAY;
440 if (types.length > 0) {
441 params = new Parameter[types.length];
442 for (int i = 0; i < params.length; i++) {
443 params[i] = makeParameter(cu, types[i], cls[i], parameterAnnotations[i], i);
444 }
445 }
446 return params;
447 }
448
449 private Parameter makeParameter(CompileUnit cu, Type type, Class cl, Annotation[] annotations, int idx) {
450 ClassNode cn = makeClassNode(cu, type, cl);
451 Parameter parameter = new Parameter(cn, "param" + idx);
452 setAnnotationMetaData(annotations, parameter);
453 return parameter;
454 }
455
456 public void invalidateCallSites() {}
457
458 @Override
459 public Object getInvokeSpecialHandle(Method m, Object receiver){
460 throw new GroovyBugError("getInvokeSpecialHandle requires at least JDK 7 wot private access to Lookup");
461 }
462
463 @Override
464 public int getVersion() {
465 return 5;
466 }
467
468 @Override
469 public Object invokeHandle(Object handle, Object[] args) throws Throwable {
470 throw new GroovyBugError("invokeHandle requires at least JDK 7");
471 }
472 }
473